package icasue.tools.sqlhelper.adaptor.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import icasue.reflect.handles.HandleSupplier;
import icasue.reflect.handles.classes.ClassOF;
import icasue.reflect.handles.fields.FieldOF;
import icasue.reflect.handles.method.MethodOF;
import icasue.tools.sqlhelper.adaptor.annotation.AutoProviderMetadata;
import icasue.tools.sqlhelper.adaptor.ibatis.MybatisAdaptor;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2021/11/15 下午3:20
 * @UpdateDate:
 * @Description: 提供默认的数据库持久层切面，不在使用icasue-plugin-stronger-advice的应用中可以注册该实例完成代理工作.
 */
@Aspect
public class DatabaseOperationAspect {

    private static ThreadLocal<AutoProviderMetadata> metadataStoreRef;
    private static Class MapperProxy;
    private static MethodHandle $digestMapperProxy;
    private static MethodHandle $getMapperInterface;

    static {
        try {
            metadataStoreRef = MybatisAdaptor.getInstance().metadataStore();
            MapperProxy = (Class) FieldOF.get_ofTp_fName_instance_.apply(MybatisAdaptor.class,"MapperProxy",null);

            Method method_digestMapperProxy = ClassOF.getDeclaredMethod_.apply(MybatisAdaptor.class, "digestMapperProxy", new Class[]{Object.class});
            MethodOF.setAccessible_.accept(method_digestMapperProxy);
            MethodHandle handle_digestMapperProxy = HandleSupplier.methodLookup.unreflect(method_digestMapperProxy);
            $digestMapperProxy = handle_digestMapperProxy;

            Field field_mapperInterface = FieldOF.findField_ofTp_fName_.apply(MapperProxy, "mapperInterface");
            FieldOF.setAccessible_.accept(field_mapperInterface);
            $getMapperInterface = HandleSupplier.methodLookup.unreflectGetter(field_mapperInterface);
        }catch (Throwable e){
            e.printStackTrace();
        }
    }

    @Pointcut(value = "@annotation(icasue.tools.sqlhelper.adaptor.annotation.AutoProvider)")
    public void $cutPoint_to_provider(){

    }

    @Before(value = "$cutPoint_to_provider()")
    public void beforeExecute(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        Object target = joinPoint.getTarget();
        Class mapperInterface = null;

        AutoProviderMetadata metadata = method.getAnnotation(AutoProviderMetadata.class);
        if(metadata == null && target != null && Proxy.class.isInstance(target)){
            try {
                Object mapperProxy = $digestMapperProxy.invoke(joinPoint.getTarget());
                if(mapperProxy != null && MapperProxy.isInstance(mapperProxy)){
                    mapperInterface = (Class)$getMapperInterface.bindTo(mapperProxy).invoke();
                    metadata = (AutoProviderMetadata)mapperInterface.getAnnotation(AutoProviderMetadata.class);
                }
            }catch (Throwable e){
                /* Here has no exception.*/
            }
        }

        if(metadata != null){
            metadataStoreRef.set(metadata);
        }
    }

    @AfterThrowing(value = "$cutPoint_to_provider()")
    public void afterThrowing(JoinPoint joinPoint){
        metadataStoreRef.remove();
    }


    @AfterReturning(value = "$cutPoint_to_provider()")
    public void afterReturning(JoinPoint joinPoint){
        metadataStoreRef.remove();
    }

}
